package martingale

import "testing"

func TestManagerStartAndTriggers(t *testing.T) {
	cfg := Config{
		Symbol:         "BTCUSDT",
		Side:           "long",
		BaseOrderUSD:   100,
		Multiplier:     2,
		MaxSteps:       3,
		GridSpacingPct: 1.0,
		TakeProfitPct:  0.5,
		StopLossPct:    5.0,
		Leverage:       3,
	}

	mgr := NewManager(nil)
	plan, actions, err := mgr.Start(cfg, 100, 3)
	if err != nil {
		t.Fatalf("start failed: %v", err)
	}
	if plan == nil {
		t.Fatal("plan should not be nil")
	}
	if len(actions) != 2 {
		t.Fatalf("expected 2 actions (Open + Limit), got %d", len(actions))
	}
	if actions[0].Type != ActionOpen {
		t.Fatalf("expected open action, got %s", actions[0].Type)
	}
	if actions[1].Type != ActionPlaceLimit {
		t.Fatalf("expected place_limit action, got %s", actions[1].Type)
	}

	qty := actions[0].PositionSizeUSD / 100
	updates, err := mgr.NotifyFill(plan.ID, 0, 100, qty)
	if err != nil {
		t.Fatalf("notify fill failed: %v", err)
	}
	if len(updates) != 2 {
		t.Fatalf("expected 2 update actions, got %d", len(updates))
	}

	// TickAll shouldn't trigger Open anymore, as we use Limit orders.
	// But we can simulate Step 1 fill.

	// Simulate Step 1 fill (Limit order filled)
	// Step 1 size = 100 * 2 = 200
	step1Qty := 200.0 / 98.5
	updates2, err := mgr.NotifyFill(plan.ID, 1, 98.5, step1Qty)
	if err != nil {
		t.Fatalf("notify fill step 1 failed: %v", err)
	}

	// Expect: UpdateStop, UpdateTake, PlaceLimit (Step 2)
	foundLimit := false
	for _, act := range updates2 {
		if act.Type == ActionPlaceLimit && act.StepIndex == 2 {
			foundLimit = true
			break
		}
	}
	if !foundLimit {
		t.Fatalf("expected place_limit action for step 2, got %#v", updates2)
	}

	plan.CurrentTakeProfit = 105
	plan.TotalQty = 1
	if !plan.shouldTakeProfit(106) {
		t.Fatalf("plan should signal take profit, plan=%+v", plan)
	}
	closeActs := mgr.TickAll(map[string]float64{"BTCUSDT": 106})
	closeFound := false
	for _, act := range closeActs {
		if act.Type == ActionCloseAll {
			closeFound = true
			break
		}
	}
	if !closeFound {
		t.Fatalf("expected close action, got %#v", closeActs)
	}
}

func TestUpdateStepPriceCancelsAndPlaces(t *testing.T) {
	cfg := Config{
		Symbol:         "TESTUSDT",
		Side:           "long",
		BaseOrderUSD:   12,
		Multiplier:     1.5,
		MaxSteps:       3,
		GridSpacingPct: 1.0,
		TakeProfitPct:  1.0,
		StopLossPct:    10.0,
		Leverage:       3,
	}

	mgr := NewManager(nil)
	plan, _, err := mgr.CreatePlan(cfg, 100, 3)
	if err != nil {
		t.Fatalf("CreatePlan failed: %v", err)
	}
	if plan == nil {
		t.Fatal("plan should not be nil")
	}
	// Simulate Step 1 has an existing Order
	plan.Steps[1].OrderID = 12345
	plan.Steps[1].Pending = true
	if err := mgr.RegisterPlan(plan); err != nil {
		t.Fatalf("RegisterPlan failed: %v", err)
	}

	// Update step price -> Should return cancel + place_limit actions
	newPrice := plan.Steps[1].TriggerPrice * 0.98
	acts, err := mgr.UpdateStepPrice(plan.ID, 1, newPrice)
	if err != nil {
		t.Fatalf("UpdateStepPrice failed: %v", err)
	}

	if len(acts) != 2 {
		t.Fatalf("expected 2 actions (cancel + place_limit), got %d", len(acts))
	}
	if acts[0].Type != ActionCancelOrder {
		t.Fatalf("expected first action cancel_order, got %s", acts[0].Type)
	}
	if acts[1].Type != ActionPlaceLimit {
		t.Fatalf("expected second action place_limit, got %s", acts[1].Type)
	}

	// Verify plan state updated
	if plan.Steps[1].OrderID != 0 {
		t.Fatalf("expected step.OrderID reset to 0, got %d", plan.Steps[1].OrderID)
	}
	if !plan.Steps[1].Pending {
		t.Fatalf("expected step.Pending=true, got false")
	}
}

func TestUpdateStepPriceStopLossValidation(t *testing.T) {
	mgr := NewManager(nil)

	// 1. Test Long Plan Validation
	cfgLong := Config{
		Symbol:         "LONG_TEST",
		Side:           "long",
		BaseOrderUSD:   100,
		Multiplier:     1.1,
		MaxSteps:       3,
		GridSpacingPct: 1.0,
		TakeProfitPct:  10.0,
		StopLossPct:    5.0, // Stop at 5% loss
		Leverage:       1,
	}
	planLong, _, err := mgr.CreatePlan(cfgLong, 100, 1) // Entry 100
	if err != nil {
		t.Fatalf("CreatePlan failed: %v", err)
	}
	mgr.RegisterPlan(planLong)

	// Fill Step 0 to set CurrentStopLoss
	// StopLoss = 100 * (1 - 0.05) = 95.0
	mgr.NotifyFill(planLong.ID, 0, 100, 1)

	if planLong.CurrentStopLoss != 95.0 {
		t.Fatalf("Expected CurrentStopLoss 95.0, got %f", planLong.CurrentStopLoss)
	}

	// Try to update Step 1 to 95.0 (Hit StopLoss) -> Should Fail
	_, err = mgr.UpdateStepPrice(planLong.ID, 1, 95.0)
	if err == nil {
		t.Fatalf("Expected error when updating price to stop loss, got nil")
	}

	// Try to update Step 1 to 95.09 (Too close, < 0.1% buffer) -> Should Fail
	// Buffer: 95.0 * 1.001 = 95.095
	_, err = mgr.UpdateStepPrice(planLong.ID, 1, 95.09)
	if err == nil {
		t.Fatalf("Expected error when updating price too close to stop loss, got nil")
	}

	// Try to update Step 1 to 96.0 (Safe) -> Should Pass
	_, err = mgr.UpdateStepPrice(planLong.ID, 1, 96.0)
	if err != nil {
		t.Fatalf("Expected success, got error: %v", err)
	}

	// 2. Test Short Plan Validation
	cfgShort := Config{
		Symbol:         "SHORT_TEST",
		Side:           "short",
		BaseOrderUSD:   100,
		Multiplier:     1.1,
		MaxSteps:       3,
		GridSpacingPct: 1.0,
		TakeProfitPct:  10.0,
		StopLossPct:    5.0, // Stop at 5% loss
		Leverage:       1,
	}
	planShort, _, err := mgr.CreatePlan(cfgShort, 100, 1) // Entry 100
	if err != nil {
		t.Fatalf("CreatePlan failed: %v", err)
	}
	mgr.RegisterPlan(planShort)

	// Fill Step 0 to set CurrentStopLoss
	// StopLoss = 100 * (1 + 0.05) = 105.0
	mgr.NotifyFill(planShort.ID, 0, 100, 1)

	if planShort.CurrentStopLoss != 105.0 {
		t.Fatalf("Expected CurrentStopLoss 105.0, got %f", planShort.CurrentStopLoss)
	}

	// Try to update Step 1 to 105.0 (Hit StopLoss) -> Should Fail
	_, err = mgr.UpdateStepPrice(planShort.ID, 1, 105.0)
	if err == nil {
		t.Fatalf("Expected error when updating price to stop loss, got nil")
	}

	// Try to update Step 1 to 104.9 (Too close, < 0.1% buffer) -> Should Fail
	// Buffer: 105.0 * 0.999 = 104.895
	// Wait, 105 * (1 - 0.001) = 104.895.
	// So 104.9 is > 104.895, which means it is CLOSER to 105.0 than the buffer allowed?
	// Short: newPrice must be < maxAllowed.
	// maxAllowed = 104.895.
	// 104.9 > 104.895 -> Invalid (too high, too close to 105).
	_, err = mgr.UpdateStepPrice(planShort.ID, 1, 104.9)
	if err == nil {
		t.Fatalf("Expected error when updating price too close to stop loss, got nil")
	}

	// Try to update Step 1 to 104.0 (Safe) -> Should Pass
	_, err = mgr.UpdateStepPrice(planShort.ID, 1, 104.0)
	if err != nil {
		t.Fatalf("Expected success, got error: %v", err)
	}
}
